package com.xiyu.service.framework.postOffice.protocol;

import com.xiyu.service.model.mailbox.account.MailboxAccount;
import jakarta.mail.*;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.search.*;
import jakarta.mail.util.SharedByteArrayInputStream;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.angus.mail.imap.AppendUID;
import org.eclipse.angus.mail.imap.IMAPFolder;
import org.eclipse.angus.mail.imap.IMAPStore;

import java.util.*;

import static com.xiyu.service.errorCode.mailbox.PostOfficeErrorCode.*;
import static com.xiyu.service.framework.exception.util.ServiceExceptionUtil.exception;

@Slf4j
public class ImapProtocol implements ReceiveProtocol {
    private final IMAPStore store;

    private final Session session;

    private Authenticator getAuth(String account, String password)  {
        return new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(account, password);
            }
        };
    }

    public ImapProtocol(MailboxAccount account) {
        Properties props = new Properties();
        props.put("mail.store.protocol", "imap");
        props.put("mail.imap.host", account.receiveAddress());
        props.put("mail.imap.port", account.receivePort());
        props.put("mail.imap.ssl.enable", account.isReceiveSecure());
        props.put("mail.imap.partialfetch", false);

        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", account.isStarttls());
        props.put("mail.smtp.ssl.enable", account.isSendSecure());
        props.put("mail.smtp.host", account.sendAddress());
        props.put("mail.smtp.port", account.sendPort());
        try {
            session = Session.getInstance(props, getAuth(account.mailboxAccount(), account.mailboxPassword()));
            store = (IMAPStore)session.getStore("imap");
            store.connect();
            HashMap<String, String> IAM = new HashMap<>();
            IAM.put("name","xiyucrm");
            IAM.put("version","1.0.0");
            IAM.put("vendor","client");
            store.id(IAM);
        } catch (AuthenticationFailedException e) {
            throw exception(POST_OFFICE_PASSWORD_ERROR);

        } catch (MessagingException e) {
            throw exception(POST_OFFICE_CONNECT_ERROR);
        }
    }

    @Override
    public void openFolder(Folder postOfficeFolder, int mode) {
        try {
            IMAPFolder folder = (IMAPFolder) postOfficeFolder;
            if (!folder.isOpen())
                folder.open(mode);
        }catch (MessagingException e) {
            e.printStackTrace();
            throw exception(POST_OFFICE_OPEN_FOLDER_ERROR);
        }
    }

    @Override
    public void closeFolder(Folder postOfficeFolder, boolean expunge) {
        try {
            IMAPFolder folder = (IMAPFolder) postOfficeFolder;
            if(folder.isOpen())
                folder.close(expunge);
        }catch (MessagingException e) {
            e.printStackTrace();
            throw exception(POST_OFFICE_CLOSE_FOLDER_ERROR);
        }
    }

    @Override
    public void setMessagesFlag(Folder postOfficeFolder, Message[] messages, Flags flags, boolean isTrue) {
        try {
            IMAPFolder imapFolder = (IMAPFolder) postOfficeFolder;
            imapFolder.setFlags(messages, flags, isTrue);
        }catch (MessagingException e) {
            e.printStackTrace();
            throw exception(POST_OFFICE_SET_FLAG_ERROR);
        }
    }

    @Override
    public Message[] getMessagesByUid(Folder postOfficeFolder, String[] uniqueIds) {
        try {
            IMAPFolder imapFolder = (IMAPFolder) postOfficeFolder;
            long[] longUniqueIds = Arrays.stream(uniqueIds).mapToLong(uid-> Long.parseLong(String.valueOf(uid))).toArray();
            return imapFolder.getMessagesByUID(longUniqueIds);
        }catch (MessagingException e) {
            e.printStackTrace();
            throw exception(POST_OFFICE_GET_MESSAGE_ERROR);
        }
    }

    @Override
    public long[] moveMessages(Folder sourcePostOfficeFolder, Folder targetPostOfficeFolder, Message[] messages) {
        try {
            IMAPFolder imapSourceFolder = (IMAPFolder) sourcePostOfficeFolder;
            IMAPFolder imapTargetFolder = (IMAPFolder) targetPostOfficeFolder;
            // 复制邮件
            AppendUID[] appendUIDS = imapSourceFolder.copyUIDMessages(messages, imapTargetFolder);
            long[] uids = Arrays.stream(appendUIDS)
                    .filter(appendUID -> appendUID.uidvalidity > 0)
                    .mapToLong(appendUID -> appendUID.uid).toArray();
            if(uids.length != messages.length){
                throw exception(POST_OFFICE_MOVE_MESSAGE_ERROR);
            }
            // 删除源邮件
//            imapSourceFolder.setFlags(messages, new Flags(Flags.Flag.DELETED), true);
            return uids;
        }catch (MessagingException e) {
            e.printStackTrace();
            throw exception(POST_OFFICE_MOVE_MESSAGE_ERROR);
        }
    }

    @Override
    public void deleteMessages(Folder postOfficeFolder, Message[] messages) {
        try {
            IMAPFolder imapFolder = (IMAPFolder) postOfficeFolder;
            imapFolder.setFlags(messages, new Flags(Flags.Flag.DELETED), true);
        }catch (MessagingException e) {
            e.printStackTrace();
            throw exception(POST_OFFICE_DELETE_MESSAGE_ERROR);
        }
    }

    @Override
    public Message createMessage() {
        return new MimeMessage(session);
    }

    @Override
    public Message createMessage(SharedByteArrayInputStream inputStream) throws MessagingException {
        return new MimeMessage(session, inputStream);
    }

    @Override
    public String getNextUid(Folder postOfficeFolder) {
        try {
            IMAPFolder imapFolder = (IMAPFolder) postOfficeFolder;
            long uid = imapFolder.getUIDNext();
            return String.valueOf(uid);
        }catch (MessagingException e) {
            e.printStackTrace();
            throw exception(POST_OFFICE_GET_MESSAGE_UID_ERROR);
        }
    }

    @Override
    public List<Folder> getAllFolder() {
        try {
            return getChildFolder(store.getDefaultFolder().list());
        }catch (MessagingException e) {
            throw exception(POST_OFFICE_GET_DEFAULT_FOLDER_ERROR);
        }
    }

    private List<Folder> getChildFolder(Folder[] folders) throws MessagingException {
        List<Folder> allFolders = new ArrayList<>(Arrays.asList(folders));
        for(Folder folder: folders){
            allFolders.addAll(getChildFolder(folder.list()));
        }
        return allFolders;
    }

    @Override
    public String createFolder(String parentFolderName, String newFolderName){
        Folder newFolder = newFolder(parentFolderName, newFolderName);
        // 检查文件夹是否存在
        try {
            if (!newFolder.exists()) {
                newFolder.create(Folder.HOLDS_MESSAGES | Folder.HOLDS_FOLDERS); // 创建子文件夹
            } else {
                return getFolderName(newFolder);
            }

        } catch (MessagingException e) {
            throw exception(POST_OFFICE_CREATE_FOLDER_FAIL);
        }
        return getFolderName(newFolder);
    }

    @Override
    public String renameFolder(String parentFolderName, String folderName, String newFolderName) {
        Folder folder;
        try {
            folder = store.getFolder(folderName);
        } catch (MessagingException e) {
            throw exception(POST_OFFICE_GET_FOLDER_ERROR);
        }
        Folder newFolder = newFolder(parentFolderName, newFolderName);
        try {
            // 检查文件夹是否存在
            if (!newFolder.exists()) {
                folder.renameTo(newFolder);
            } else {
                throw exception(POST_OFFICE_EXISTS_DUP_FOLDER);
            }
            return getFolderName(newFolder);
        } catch (MessagingException e) {
            throw exception(POST_OFFICE_RENAME_FOLDER_FAIL);
        }

    }

    @Override
    public void deleteFolder(String folderName) {
        Folder folder;
        try {
            folder = store.getFolder(folderName);
            folder.delete(true);
        } catch (MessagingException e) {
            e.printStackTrace();
            throw exception(POST_OFFICE_DELETE_FOLDER_ERROR);
        }
    }

    @Override
    public Folder getRootFolder() {
        try {
            return store.getDefaultFolder();
        } catch (MessagingException e) {
            throw exception(POST_OFFICE_GET_FOLDER_ERROR);
        }
    }

    @Override
    public String getFolderName(Folder postOfficeFolder) {
        String folderName = postOfficeFolder.getFullName();
        if(folderName.isEmpty())
            return folderName;
        if(folderName.charAt(0) == '/')
            return folderName.substring(1);
        return folderName;
    }

    @Override
    public String[] getFolderAttributes(Folder postOfficeFolder) {
        IMAPFolder imapFolder = (IMAPFolder)postOfficeFolder;
        try {
            return imapFolder.getAttributes();
        } catch (MessagingException e) {
            throw exception(POST_OFFICE_GET_FOLDER_ERROR);
        }
    }

    @Override
    public LinkedHashMap<String, Message> getFolderMessages(Folder postOfficeFolder, int begin, int end) {
        try {
            IMAPFolder folder = (IMAPFolder)postOfficeFolder;
            int messageCount = folder.getMessageCount();
            Message[] messages = folder.getMessages(begin, Math.min(end, messageCount));
            return batchGetUidMessage(folder, messages);
        } catch (Exception e) {
            throw exception(POST_OFFICE_RECEIVE_FOLDER_MAIL_ERROR);
        }
    }


    @Override
    public int getFolderMailCount(Folder postOfficeFolder){
        try {
            IMAPFolder folder = (IMAPFolder)postOfficeFolder;
            return folder.getMessageCount();
        } catch (MessagingException e) {
            e.printStackTrace();
            throw exception(POST_OFFICE_RECEIVE_FOLDER_MAIL_ERROR);
        }
    }

    @Override
    public int getFolderUnreadMailCount(Folder postOfficeFolder) {
        try {
            IMAPFolder folder = (IMAPFolder)postOfficeFolder;
            return folder.getUnreadMessageCount();
        } catch (Exception e) {
            e.printStackTrace();
            throw exception(POST_OFFICE_RECEIVE_FOLDER_MAIL_ERROR);
        }
    }

    @Override
    public int getFolderTopMailCount(Folder postOfficeFolder) {
        try {
            IMAPFolder folder = (IMAPFolder)postOfficeFolder;
            Flags flagged = new Flags(Flags.Flag.FLAGGED);
            FlagTerm flagTerm = new FlagTerm(flagged, true);
            Message[] messages = folder.search(flagTerm);
            return messages.length;
        } catch (Exception e) {
            throw exception(POST_OFFICE_RECEIVE_FOLDER_MAIL_ERROR);
        }
    }

    @SneakyThrows
    @Override
    public void testGetMessage() {
//        IMAPFolder folder = getFolderByName("其他文件夹");
//        openFolder(folder, Folder.READ_ONLY);
//        closeFolder(folder, false);
//        List<Folder> folders = getAllFolder();
//        for(Folder folder: folders){
//            IMAPFolder imapFolder = (IMAPFolder)folder;
////            Folder[] childFolders = imapFolder.list("aaa");
////            if(childFolders.length > 0)
////                System.out.println(imapFolder.getFullName() + " 有子文件夹 数量是" + childFolders.length);
////            else
////                System.out.println("无子文件夹");
//            System.out.println(getFolderName(imapFolder));
//            System.out.println(Arrays.toString(imapFolder.getAttributes()));
//            System.out.println("");
//        }
//        System.out.println(folders);
//        SearchTerm searchTerm = new FromStringTerm("zhaopengcheng@huice.com");
        IMAPFolder folder = getFolderByName("INBOX");
        folder.open(Folder.READ_ONLY);
        SearchTerm searchTerm = new SubjectTerm("库存数据异常");
        SearchTerm bodyTerm = new BodyTerm("a");
        FlagTerm flagTerm = new FlagTerm(new Flags(Flags.Flag.SEEN), false);
        SearchTerm bodyTerm1 = new BodyTerm("joom");
        Message[] messages = folder.search(bodyTerm1);
        System.out.println("this is message count " + messages.length);
        folder.close(false);
    }

    @Override
    public LinkedHashMap<String, Message> getFolderUnreadMessages(Folder postOfficeFolder) {
        try {
            IMAPFolder folder = (IMAPFolder)postOfficeFolder;
            Flags flagged = new Flags(Flags.Flag.SEEN);
            FlagTerm flagTerm = new FlagTerm(flagged, false);
            Message[] messages = folder.search(flagTerm);
            return batchGetUidMessage(folder, messages);
        } catch (Exception e) {
            throw exception(POST_OFFICE_RECEIVE_FOLDER_MAIL_ERROR);
        }
    }

    @Override
    public LinkedHashMap<String, Message> getFolderTopMessages(Folder postOfficeFolder) {
        try {
            IMAPFolder folder = (IMAPFolder)postOfficeFolder;
            Flags flagged = new Flags(Flags.Flag.FLAGGED);
            FlagTerm flagTerm = new FlagTerm(flagged, true);
            Message[] messages = folder.search(flagTerm);
            return batchGetUidMessage(folder, messages);
        } catch (Exception e) {
            throw exception(POST_OFFICE_RECEIVE_FOLDER_MAIL_ERROR);
        }
    }

    @Override
    public void close() {
        try {
            store.close();
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }

    private LinkedHashMap<String, Message> batchGetUidMessage(IMAPFolder folder, Message[] messages){
        try {
            LinkedHashMap<String, Message> messageMap = new LinkedHashMap<>();
            for(Message message : messages){
                long uid = folder.getUID(message);
                if(uid > 0)
                    messageMap.put(String.valueOf(uid), message);
            }
            return messageMap;
        } catch (MessagingException e) {
            throw exception(POST_OFFICE_RECEIVE_FOLDER_MAIL_ERROR);
        }
    }

    @Override
    public IMAPFolder getFolderByName(String folderName) {
        if(folderName.isEmpty())
            throw exception(POST_OFFICE_FOLDER_NAME_EMPTY);
        try {
            return (IMAPFolder)(store.getFolder(folderName));
        } catch (MessagingException e) {
            throw exception(POST_OFFICE_GET_FOLDER_ERROR);
        }
    }

    private Folder newFolder(String parentFolderName, String newFolderName){
        // 获取父文件夹
        Folder parentFolder;
        try {
            parentFolder = store.getFolder(parentFolderName);
        } catch (MessagingException e) {
            throw exception(POST_OFFICE_PARENT_FOLDER_GET_ERROR);
        }

        Folder newFolder;
        try {
            newFolder = parentFolder.getFolder(newFolderName);
        } catch (MessagingException e) {
            throw exception(POST_OFFICE_GET_FOLDER_ERROR);
        }
        return newFolder;

    }

}
